// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/logging/stats_event_subscriber.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <utility>

#include "base/memory/ref_counted.h"
#include "base/rand_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/cast/cast_environment.h"
#include "media/cast/logging/logging_defines.h"
#include "media/cast/test/fake_receiver_time_offset_estimator.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
const int kReceiverOffsetSecs = 100;
}

namespace media {
namespace cast {

    class StatsEventSubscriberTest : public ::testing::Test {
    protected:
        StatsEventSubscriberTest()
            : sender_clock_(new base::SimpleTestTickClock())
            , task_runner_(new FakeSingleThreadTaskRunner(sender_clock_))
            , cast_environment_(
                  new CastEnvironment(std::unique_ptr<base::TickClock>(sender_clock_),
                      task_runner_,
                      task_runner_,
                      task_runner_))
            , fake_offset_estimator_(
                  base::TimeDelta::FromSeconds(kReceiverOffsetSecs))
        {
            receiver_clock_.Advance(base::TimeDelta::FromSeconds(kReceiverOffsetSecs));
            cast_environment_->logger()->Subscribe(&fake_offset_estimator_);
        }

        ~StatsEventSubscriberTest() override
        {
            if (subscriber_.get())
                cast_environment_->logger()->Unsubscribe(subscriber_.get());
            cast_environment_->logger()->Unsubscribe(&fake_offset_estimator_);
        }

        void AdvanceClocks(base::TimeDelta delta)
        {
            task_runner_->Sleep(delta);
            receiver_clock_.Advance(delta);
        }

        void Init(EventMediaType event_media_type)
        {
            DCHECK(!subscriber_.get());
            subscriber_.reset(new StatsEventSubscriber(
                event_media_type, cast_environment_->Clock(), &fake_offset_estimator_));
            cast_environment_->logger()->Subscribe(subscriber_.get());
        }

        base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment.
        base::SimpleTestTickClock receiver_clock_;
        scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
        scoped_refptr<CastEnvironment> cast_environment_;
        test::FakeReceiverTimeOffsetEstimator fake_offset_estimator_;
        std::unique_ptr<StatsEventSubscriber> subscriber_;
    };

    TEST_F(StatsEventSubscriberTest, CaptureEncode)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        FrameId frame_id = FrameId::first();
        int extra_frames = 50;
        // Only the first |extra_frames| frames logged will be taken into account
        // when computing dropped frames.
        int num_frames = StatsEventSubscriber::kMaxFrameInfoMapSize + 50;
        int dropped_frames = 0;
        base::TimeTicks start_time = sender_clock_->NowTicks();
        // Drop half the frames during the encode step.
        for (int i = 0; i < num_frames; i++) {
            std::unique_ptr<FrameEvent> capture_begin_event(new FrameEvent());
            capture_begin_event->timestamp = sender_clock_->NowTicks();
            capture_begin_event->type = FRAME_CAPTURE_BEGIN;
            capture_begin_event->media_type = VIDEO_EVENT;
            capture_begin_event->rtp_timestamp = rtp_timestamp;
            cast_environment_->logger()->DispatchFrameEvent(
                std::move(capture_begin_event));

            AdvanceClocks(base::TimeDelta::FromMicroseconds(10));
            std::unique_ptr<FrameEvent> capture_end_event(new FrameEvent());
            capture_end_event->timestamp = sender_clock_->NowTicks();
            capture_end_event->type = FRAME_CAPTURE_END;
            capture_end_event->media_type = VIDEO_EVENT;
            capture_end_event->rtp_timestamp = rtp_timestamp;
            cast_environment_->logger()->DispatchFrameEvent(
                std::move(capture_end_event));

            if (i % 2 == 0) {
                AdvanceClocks(base::TimeDelta::FromMicroseconds(10));
                std::unique_ptr<FrameEvent> encode_event(new FrameEvent());
                encode_event->timestamp = sender_clock_->NowTicks();
                encode_event->type = FRAME_ENCODED;
                encode_event->media_type = VIDEO_EVENT;
                encode_event->rtp_timestamp = rtp_timestamp;
                encode_event->frame_id = frame_id;
                encode_event->size = 1024;
                encode_event->key_frame = true;
                encode_event->target_bitrate = 5678;
                encode_event->encoder_cpu_utilization = 9.10;
                encode_event->idealized_bitrate_utilization = 11.12;
                cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event));
            } else if (i < extra_frames) {
                dropped_frames++;
            }
            AdvanceClocks(base::TimeDelta::FromMicroseconds(34567));
            rtp_timestamp += RtpTimeDelta::FromTicks(90);
            frame_id++;
        }

        base::TimeTicks end_time = sender_clock_->NowTicks();

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::CAPTURE_FPS);
        ASSERT_TRUE(it != stats_map.end());

        base::TimeDelta duration = end_time - start_time;
        EXPECT_DOUBLE_EQ(
            it->second,
            static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);

        it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_CAPTURED);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_frames));

        it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_DROPPED_BY_ENCODER);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(dropped_frames));

        it = stats_map.find(StatsEventSubscriber::AVG_CAPTURE_LATENCY_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(0.01));
    }

    TEST_F(StatsEventSubscriberTest, Encode)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        FrameId frame_id = FrameId::first();
        int num_frames = 10;
        base::TimeTicks start_time = sender_clock_->NowTicks();
        AdvanceClocks(base::TimeDelta::FromMicroseconds(35678));
        base::TimeTicks first_event_time = sender_clock_->NowTicks();
        base::TimeTicks last_event_time;
        int total_size = 0;
        for (int i = 0; i < num_frames; i++) {
            int size = 1000 + base::RandInt(-100, 100);
            total_size += size;
            std::unique_ptr<FrameEvent> encode_event(new FrameEvent());
            encode_event->timestamp = sender_clock_->NowTicks();
            encode_event->type = FRAME_ENCODED;
            encode_event->media_type = VIDEO_EVENT;
            encode_event->rtp_timestamp = rtp_timestamp;
            encode_event->frame_id = frame_id;
            encode_event->size = size;
            encode_event->key_frame = true;
            encode_event->target_bitrate = 5678;
            encode_event->encoder_cpu_utilization = 9.10;
            encode_event->idealized_bitrate_utilization = 11.12;
            cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event));
            last_event_time = sender_clock_->NowTicks();

            AdvanceClocks(base::TimeDelta::FromMicroseconds(35678));
            rtp_timestamp += RtpTimeDelta::FromTicks(90);
            frame_id++;
        }

        base::TimeTicks end_time = sender_clock_->NowTicks();

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::ENCODE_FPS);
        ASSERT_TRUE(it != stats_map.end());

        base::TimeDelta duration = end_time - start_time;
        EXPECT_DOUBLE_EQ(
            it->second,
            static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);

        it = stats_map.find(StatsEventSubscriber::ENCODE_KBPS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second,
            static_cast<double>(total_size) / duration.InMillisecondsF() * 8);

        it = stats_map.find(StatsEventSubscriber::FIRST_EVENT_TIME_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second,
            (first_event_time - base::TimeTicks::UnixEpoch()).InMillisecondsF());

        it = stats_map.find(StatsEventSubscriber::LAST_EVENT_TIME_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second,
            (last_event_time - base::TimeTicks::UnixEpoch()).InMillisecondsF());
    }

    TEST_F(StatsEventSubscriberTest, Decode)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        FrameId frame_id = FrameId::first();
        int num_frames = 10;
        base::TimeTicks start_time = sender_clock_->NowTicks();
        for (int i = 0; i < num_frames; i++) {
            std::unique_ptr<FrameEvent> decode_event(new FrameEvent());
            decode_event->timestamp = receiver_clock_.NowTicks();
            decode_event->type = FRAME_DECODED;
            decode_event->media_type = VIDEO_EVENT;
            decode_event->rtp_timestamp = rtp_timestamp;
            decode_event->frame_id = frame_id;
            cast_environment_->logger()->DispatchFrameEvent(std::move(decode_event));

            AdvanceClocks(base::TimeDelta::FromMicroseconds(36789));
            rtp_timestamp += RtpTimeDelta::FromTicks(90);
            frame_id++;
        }

        base::TimeTicks end_time = sender_clock_->NowTicks();

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::DECODE_FPS);
        ASSERT_TRUE(it != stats_map.end());

        base::TimeDelta duration = end_time - start_time;
        EXPECT_DOUBLE_EQ(
            it->second,
            static_cast<double>(num_frames) / duration.InMillisecondsF() * 1000);
    }

    TEST_F(StatsEventSubscriberTest, PlayoutDelay)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        FrameId frame_id = FrameId::first();
        int num_frames = 10;
        int late_frames = 0;
        for (int i = 0, delay_ms = -50; i < num_frames; i++, delay_ms += 10) {
            base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms);
            if (delay_ms > 0)
                late_frames++;
            std::unique_ptr<FrameEvent> playout_event(new FrameEvent());
            playout_event->timestamp = receiver_clock_.NowTicks();
            playout_event->type = FRAME_PLAYOUT;
            playout_event->media_type = VIDEO_EVENT;
            playout_event->rtp_timestamp = rtp_timestamp;
            playout_event->frame_id = frame_id;
            playout_event->delay_delta = delay;
            cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event));

            AdvanceClocks(base::TimeDelta::FromMicroseconds(37890));
            rtp_timestamp += RtpTimeDelta::FromTicks(90);
            frame_id++;
        }

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(
            StatsEventSubscriber::NUM_FRAMES_LATE);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, late_frames);
    }

    TEST_F(StatsEventSubscriberTest, E2ELatency)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        FrameId frame_id = FrameId::first();
        int num_frames = 10;
        base::TimeDelta total_latency;
        for (int i = 0; i < num_frames; i++) {
            std::unique_ptr<FrameEvent> capture_begin_event(new FrameEvent());
            capture_begin_event->timestamp = sender_clock_->NowTicks();
            capture_begin_event->type = FRAME_CAPTURE_BEGIN;
            capture_begin_event->media_type = VIDEO_EVENT;
            capture_begin_event->rtp_timestamp = rtp_timestamp;
            cast_environment_->logger()->DispatchFrameEvent(
                std::move(capture_begin_event));

            int latency_micros = 100000 + base::RandInt(-5000, 50000);
            base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros);
            AdvanceClocks(latency);

            int delay_micros = base::RandInt(-50000, 50000);
            base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_micros);
            total_latency += latency;

            std::unique_ptr<FrameEvent> playout_event(new FrameEvent());
            playout_event->timestamp = receiver_clock_.NowTicks();
            playout_event->type = FRAME_PLAYOUT;
            playout_event->media_type = VIDEO_EVENT;
            playout_event->rtp_timestamp = rtp_timestamp;
            playout_event->frame_id = frame_id;
            playout_event->delay_delta = delay;
            cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event));

            rtp_timestamp += RtpTimeDelta::FromTicks(90);
            frame_id++;
        }

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::AVG_E2E_LATENCY_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second, total_latency.InMillisecondsF() / num_frames);
    }

    TEST_F(StatsEventSubscriberTest, Packets)
    {
        Init(VIDEO_EVENT);

        RtpTimeTicks rtp_timestamp;
        int num_packets = 10;
        int num_latency_recorded_packets = 0;
        base::TimeTicks start_time = sender_clock_->NowTicks();
        int total_size = 0;
        int retransmit_total_size = 0;
        base::TimeDelta total_network_latency;
        base::TimeDelta total_queueing_latency;
        base::TimeDelta total_packet_latency;
        int num_packets_transmitted = 0;
        int num_packets_received = 0;
        int num_packets_retransmitted = 0;
        int num_packets_rtx_rejected = 0;

        base::TimeTicks sender_encoded_time = sender_clock_->NowTicks();
        base::TimeTicks receiver_encoded_time = receiver_clock_.NowTicks();
        std::unique_ptr<FrameEvent> encode_event(new FrameEvent());
        encode_event->timestamp = sender_encoded_time;
        encode_event->type = FRAME_ENCODED;
        encode_event->media_type = VIDEO_EVENT;
        encode_event->rtp_timestamp = rtp_timestamp;
        encode_event->frame_id = FrameId::first();
        cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event));

        // Every 2nd packet will be retransmitted once.
        // Every 4th packet will be retransmitted twice.
        // Every 8th packet will be retransmitted 3 times + 1 rejected retransmission.
        for (int i = 0; i < num_packets; i++) {
            int size = 1000 + base::RandInt(-100, 100);
            total_size += size;

            std::unique_ptr<PacketEvent> send_event(new PacketEvent());
            send_event->timestamp = sender_clock_->NowTicks();
            send_event->type = PACKET_SENT_TO_NETWORK;
            send_event->media_type = VIDEO_EVENT;
            send_event->rtp_timestamp = rtp_timestamp;
            send_event->frame_id = FrameId::first();
            send_event->packet_id = i;
            send_event->max_packet_id = num_packets - 1;
            send_event->size = size;
            cast_environment_->logger()->DispatchPacketEvent(std::move(send_event));

            num_packets_transmitted++;
            total_queueing_latency += sender_clock_->NowTicks() - sender_encoded_time;

            int latency_micros = 20000 + base::RandInt(-10000, 10000);
            base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros);
            // Latency is only recorded for packets that aren't retransmitted.
            if (i % 2 != 0) {
                total_network_latency += latency;
                total_packet_latency += receiver_clock_.NowTicks() - receiver_encoded_time + latency;
                num_latency_recorded_packets++;
            }

            AdvanceClocks(latency);

            base::TimeTicks received_time = receiver_clock_.NowTicks();

            // Retransmission 1.
            AdvanceClocks(base::TimeDelta::FromMicroseconds(12345));
            if (i % 2 == 0) {
                std::unique_ptr<PacketEvent> retransmit_event(new PacketEvent());
                retransmit_event->timestamp = receiver_clock_.NowTicks();
                retransmit_event->type = PACKET_RETRANSMITTED;
                retransmit_event->media_type = VIDEO_EVENT;
                retransmit_event->rtp_timestamp = rtp_timestamp;
                retransmit_event->frame_id = FrameId::first();
                retransmit_event->packet_id = i;
                retransmit_event->max_packet_id = num_packets - 1;
                retransmit_event->size = size;
                cast_environment_->logger()->DispatchPacketEvent(
                    std::move(retransmit_event));

                retransmit_total_size += size;
                num_packets_transmitted++;
                num_packets_retransmitted++;
            }

            // Retransmission 2.
            AdvanceClocks(base::TimeDelta::FromMicroseconds(13456));
            if (i % 4 == 0) {
                std::unique_ptr<PacketEvent> retransmit_event(new PacketEvent());
                retransmit_event->timestamp = receiver_clock_.NowTicks();
                retransmit_event->type = PACKET_RETRANSMITTED;
                retransmit_event->media_type = VIDEO_EVENT;
                retransmit_event->rtp_timestamp = rtp_timestamp;
                retransmit_event->frame_id = FrameId::first();
                retransmit_event->packet_id = i;
                retransmit_event->max_packet_id = num_packets - 1;
                retransmit_event->size = size;
                cast_environment_->logger()->DispatchPacketEvent(
                    std::move(retransmit_event));

                retransmit_total_size += size;
                num_packets_transmitted++;
                num_packets_retransmitted++;
            }

            // Retransmission 3.
            AdvanceClocks(base::TimeDelta::FromMicroseconds(14567));
            if (i % 8 == 0) {
                std::unique_ptr<PacketEvent> retransmit_event(new PacketEvent());
                retransmit_event->timestamp = receiver_clock_.NowTicks();
                retransmit_event->type = PACKET_RETRANSMITTED;
                retransmit_event->media_type = VIDEO_EVENT;
                retransmit_event->rtp_timestamp = rtp_timestamp;
                retransmit_event->frame_id = FrameId::first();
                retransmit_event->packet_id = i;
                retransmit_event->max_packet_id = num_packets - 1;
                retransmit_event->size = size;
                cast_environment_->logger()->DispatchPacketEvent(
                    std::move(retransmit_event));

                std::unique_ptr<PacketEvent> reject_event(new PacketEvent());
                reject_event->timestamp = receiver_clock_.NowTicks();
                reject_event->type = PACKET_RTX_REJECTED;
                reject_event->media_type = VIDEO_EVENT;
                reject_event->rtp_timestamp = rtp_timestamp;
                reject_event->frame_id = FrameId::first();
                reject_event->packet_id = i;
                reject_event->max_packet_id = num_packets - 1;
                reject_event->size = size;
                cast_environment_->logger()->DispatchPacketEvent(std::move(reject_event));

                retransmit_total_size += size;
                num_packets_transmitted++;
                num_packets_retransmitted++;
                num_packets_rtx_rejected++;
            }

            std::unique_ptr<PacketEvent> receive_event(new PacketEvent());
            receive_event->timestamp = received_time;
            receive_event->type = PACKET_RECEIVED;
            receive_event->media_type = VIDEO_EVENT;
            receive_event->rtp_timestamp = rtp_timestamp;
            receive_event->frame_id = FrameId::first();
            receive_event->packet_id = i;
            receive_event->max_packet_id = num_packets - 1;
            receive_event->size = size;
            cast_environment_->logger()->DispatchPacketEvent(std::move(receive_event));

            num_packets_received++;
        }

        base::TimeTicks end_time = sender_clock_->NowTicks();
        base::TimeDelta duration = end_time - start_time;

        StatsEventSubscriber::StatsMap stats_map;
        subscriber_->GetStatsInternal(&stats_map);

        // Measure AVG_NETWORK_LATENCY_MS, TRANSMISSION_KBPS, RETRANSMISSION_KBPS.
        StatsEventSubscriber::StatsMap::iterator it = stats_map.find(StatsEventSubscriber::AVG_NETWORK_LATENCY_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second,
            total_network_latency.InMillisecondsF() / num_latency_recorded_packets);

        it = stats_map.find(StatsEventSubscriber::AVG_QUEUEING_LATENCY_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second,
            total_queueing_latency.InMillisecondsF() / num_packets);

        it = stats_map.find(StatsEventSubscriber::AVG_PACKET_LATENCY_MS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(
            it->second,
            total_packet_latency.InMillisecondsF() / num_latency_recorded_packets);

        it = stats_map.find(StatsEventSubscriber::TRANSMISSION_KBPS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second,
            static_cast<double>(total_size) / duration.InMillisecondsF() * 8);

        it = stats_map.find(StatsEventSubscriber::RETRANSMISSION_KBPS);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second,
            static_cast<double>(retransmit_total_size) / duration.InMillisecondsF() * 8);

        it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_SENT);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets));

        it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RECEIVED);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets_received));

        it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RETRANSMITTED);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets_retransmitted));

        it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RTX_REJECTED);
        ASSERT_TRUE(it != stats_map.end());

        EXPECT_DOUBLE_EQ(it->second, static_cast<double>(num_packets_rtx_rejected));
    }

    bool CheckHistogramHasValue(base::ListValue* values,
        const std::string& bucket, int expected_count)
    {
        for (size_t i = 0; i < values->GetSize(); ++i) {
            const base::DictionaryValue* dict = NULL;
            values->GetDictionary(i, &dict);
            if (!dict->HasKey(bucket))
                continue;
            int bucket_count = 0;
            if (!dict->GetInteger(bucket, &bucket_count))
                return false;
            return bucket_count == expected_count;
        }
        return false;
    }

    TEST_F(StatsEventSubscriberTest, Histograms)
    {
        Init(VIDEO_EVENT);
        AdvanceClocks(base::TimeDelta::FromMilliseconds(123));

        RtpTimeTicks rtp_timestamp = RtpTimeTicks().Expand(UINT32_C(123));
        FrameId frame_id = FrameId::first();

        // 10 Frames with capture latency in the bucket of "10-14"ms.
        // 10 Frames with encode time in the bucket of "15-19"ms.
        for (int i = 0; i < 10; ++i) {
            ++frame_id;
            rtp_timestamp += RtpTimeDelta::FromTicks(1);

            std::unique_ptr<FrameEvent> capture_begin_event(new FrameEvent());
            capture_begin_event->timestamp = sender_clock_->NowTicks();
            capture_begin_event->type = FRAME_CAPTURE_BEGIN;
            capture_begin_event->media_type = VIDEO_EVENT;
            capture_begin_event->rtp_timestamp = rtp_timestamp;
            cast_environment_->logger()->DispatchFrameEvent(
                std::move(capture_begin_event));

            AdvanceClocks(base::TimeDelta::FromMilliseconds(10));
            std::unique_ptr<FrameEvent> capture_end_event(new FrameEvent());
            capture_end_event->timestamp = sender_clock_->NowTicks();
            capture_end_event->type = FRAME_CAPTURE_END;
            capture_end_event->media_type = VIDEO_EVENT;
            capture_end_event->rtp_timestamp = rtp_timestamp;
            cast_environment_->logger()->DispatchFrameEvent(
                std::move(capture_end_event));

            AdvanceClocks(base::TimeDelta::FromMilliseconds(15));
            std::unique_ptr<FrameEvent> encode_event(new FrameEvent());
            encode_event->timestamp = sender_clock_->NowTicks();
            encode_event->type = FRAME_ENCODED;
            encode_event->media_type = VIDEO_EVENT;
            encode_event->rtp_timestamp = rtp_timestamp;
            encode_event->frame_id = frame_id;
            encode_event->size = 1024;
            encode_event->key_frame = true;
            encode_event->target_bitrate = 5678;
            encode_event->encoder_cpu_utilization = 9.10;
            encode_event->idealized_bitrate_utilization = 11.12;
            cast_environment_->logger()->DispatchFrameEvent(std::move(encode_event));
        }

        // Send 3 packets for the last frame.
        // Queueing latencies are 100ms, 200ms and 300ms.
        for (int i = 0; i < 3; ++i) {
            AdvanceClocks(base::TimeDelta::FromMilliseconds(100));
            std::unique_ptr<PacketEvent> send_event(new PacketEvent());
            send_event->timestamp = sender_clock_->NowTicks();
            send_event->type = PACKET_SENT_TO_NETWORK;
            send_event->media_type = VIDEO_EVENT;
            send_event->rtp_timestamp = rtp_timestamp;
            send_event->frame_id = FrameId::first();
            send_event->packet_id = i;
            send_event->max_packet_id = 2;
            send_event->size = 123;
            cast_environment_->logger()->DispatchPacketEvent(std::move(send_event));
        }

        // Receive 3 packets for the last frame.
        // Network latencies are 100ms, 200ms and 300ms.
        // Packet latencies are 400ms.
        AdvanceClocks(base::TimeDelta::FromMilliseconds(100));
        for (int i = 0; i < 3; ++i) {
            std::unique_ptr<PacketEvent> receive_event(new PacketEvent());
            receive_event->timestamp = receiver_clock_.NowTicks();
            receive_event->type = PACKET_RECEIVED;
            receive_event->media_type = VIDEO_EVENT;
            receive_event->rtp_timestamp = rtp_timestamp;
            receive_event->frame_id = FrameId::first();
            receive_event->packet_id = i;
            receive_event->max_packet_id = 2;
            receive_event->size = 123;
            cast_environment_->logger()->DispatchPacketEvent(std::move(receive_event));
        }

        std::unique_ptr<FrameEvent> playout_event(new FrameEvent());
        playout_event->timestamp = receiver_clock_.NowTicks();
        playout_event->type = FRAME_PLAYOUT;
        playout_event->media_type = VIDEO_EVENT;
        playout_event->rtp_timestamp = rtp_timestamp;
        playout_event->frame_id = frame_id;
        playout_event->delay_delta = base::TimeDelta::FromMilliseconds(100);
        cast_environment_->logger()->DispatchFrameEvent(std::move(playout_event));

        StatsEventSubscriber::SimpleHistogram* histogram;
        std::unique_ptr<base::ListValue> values;

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::CAPTURE_LATENCY_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "10-14", 10));

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::ENCODE_TIME_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "15-19", 10));

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::QUEUEING_LATENCY_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1));
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "200-219", 1));
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "300-319", 1));

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::NETWORK_LATENCY_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1));
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "200-219", 1));
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "300-319", 1));

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::PACKET_LATENCY_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "400-419", 3));

        histogram = subscriber_->GetHistogramForTesting(
            StatsEventSubscriber::LATE_FRAME_MS_HISTO);
        ASSERT_TRUE(histogram);
        values = histogram->GetHistogram();
        EXPECT_TRUE(CheckHistogramHasValue(values.get(), "100-119", 1));
    }

} // namespace cast
} // namespace media
